package com.wzq.tools.tree;

import com.wzq.tools.interconversion.MapAndObjectInterconversion;
import com.wzq.tools.interconversion.ObjectAndListInterconversion;
import com.wzq.tools.string.StringHandle;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 将数据封装成一棵树
 *
 * @author weizq
 * @since 2021/2/26
 */
public class AssembleData {

    /**
     * 将list数据组装成一棵树 前提是id必须为父id
     *
     * @param objList 组装的对象（List是Map）
     * @param pidName 父id字段名，针对不同的对象可能命名不同
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @throws
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> listMapToTree(List<Map<String, Object>> mapList, String pidName) {
        // 储存根节点集合
        List<Map<String, Object>> baseNodes = new ArrayList<>();
        // 节点id->对应的节点
        HashMap<String, Map<String, Object>> sonMap = new HashMap<>(baseNodes.size());
        // 节点id->节点对应的子节点
        HashMap<String, List<Map<String, Object>>> parentMap = new HashMap<>(baseNodes.size());

        for (Map<String, Object> stringObjectMap : mapList) {
            String currentId = StringHandle.objectToString(stringObjectMap.get("id"));
            String parentId = StringHandle.objectToString(stringObjectMap.get(pidName));

            // 把节点先放在Map中,为了以后获取当前节点的父节点
            sonMap.put(currentId, stringObjectMap);

            if (parentMap.get(parentId) == null) {
                parentMap.put(parentId, new ArrayList<>());
            }
            //将子节点加入父节点
            parentMap.get(parentId).add(stringObjectMap);

            // 判断当前节点是否有父节点
            if (sonMap.get(parentId) != null) {
                List childrenList = (List) sonMap.get(parentId).get("children");
                if (childrenList == null) {
                    childrenList = new ArrayList<>();
                }
                // 将当前节点添加到父节点中
                childrenList.add(stringObjectMap);
                sonMap.get(parentId).put("children", childrenList);
            }
            // 判断当前节点是否有子节点
            if (!CollectionUtils.isEmpty(parentMap.get(currentId))) {
                //将所有的子节点加入到当前节点
                stringObjectMap.put("children", parentMap.get(currentId));
            }

            //根据节点id过滤根节点
            if (!ObjectUtils.allNotNull(stringObjectMap.get(pidName))) {
                baseNodes.add(stringObjectMap);
            }
        }
        return baseNodes;
    }


    /**
     * 将list数据组装成一棵树 前提是id必须为父id
     *
     * @param objList 组装的对象（List是对象）
     * @param pidName 父id字段名，针对不同的对象可能命名不同
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> listObjectToTree(List objList, String pidName) {
        List<Map<String, Object>> mapList = MapAndObjectInterconversion.objectsToMaps(objList);
        return listMapToTree(mapList, pidName);
    }

    /**
     * 将list数据组装成一棵树(里面统计了总数和类型数据占比) 前提是id必须为父id
     *
     * @param objList
     * @param pidName
     * @param statisticalField
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @throws
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> listStatisticalPercentage(List objList, String pidName, String statisticalField) {
        List<Map<String, Object>> mapList = MapAndObjectInterconversion.objectsToMaps(objList);
        mapList = listMapToTree(mapList, pidName);

        return statisticalPercentageAndInorder(mapList, statisticalField);
    }

    /**
     * 利用递归遍历树，统计每个层级的总数，数据层级比较多时，不建议使用。
     *
     * @param root 遍历对象
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> inorder(List<Map<String, Object>> root) {
        if (CollectionUtils.isEmpty(root)) {
            return new ArrayList<>();
        }

        for (Map<String, Object> stringObjectMap : root) {
            List<Map<String, Object>> list = ObjectAndListInterconversion.objToList(stringObjectMap.get("children"));
            if (!CollectionUtils.isEmpty(list)) {
                stringObjectMap.put("total", list.size());
                inorder(list);
            }
        }
        return root;
    }

    /**
     * 利用递归遍历树，统计每个层级的某种类型数据占比，数据层级比较多时，不建议使用。
     *
     * @param root             统计对象
     * @param statisticalField 需要统计的类型字段
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @throws
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> statisticalPercentage(List<Map<String, Object>> root, String statisticalField) {
        if (CollectionUtils.isEmpty(root)) {
            return new ArrayList<>();
        }

        for (Map<String, Object> stringObjectMap : root) {
            List<Map<String, Object>> list = ObjectAndListInterconversion.objToList(stringObjectMap.get("children"));
            if (!CollectionUtils.isEmpty(list)) {
                filterData(statisticalField, stringObjectMap, list);
                statisticalPercentage(list, statisticalField);
            }
        }
        return root;
    }

    /**
     * 利用递归遍历树，统计每个层级的某种类型数据占比,并且统计总数，数据层级比较多时，不建议使用。
     *
     * @param root             统计对象
     * @param statisticalField 需要统计的类型字段
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @throws
     * @author weizq
     * @since 2021/2/26
     */
    public static List<Map<String, Object>> statisticalPercentageAndInorder(List<Map<String, Object>> root, String statisticalField) {
        if (CollectionUtils.isEmpty(root)) {
            return new ArrayList<>();
        }

        for (Map<String, Object> stringObjectMap : root) {
            List<Map<String, Object>> list = ObjectAndListInterconversion.objToList(stringObjectMap.get("children"));
            if (!CollectionUtils.isEmpty(list)) {
                filterData(statisticalField, stringObjectMap, list);
                stringObjectMap.put("total", list.size());
                statisticalPercentage(list, statisticalField);
            }
        }
        return root;
    }

    /**
     * 统计符合类型的数据
     *
     * @param statisticalField
     * @param stringObjectMap
     * @param list
     * @return void
     * @throws
     * @author weizq
     * @since 2021/2/26
     */
    private static void filterData(String statisticalField, Map<String, Object> stringObjectMap, List<Map<String, Object>> list) {
        List fieldList = list.stream().filter(item -> {
            String fieldValue = StringHandle.objectToString(stringObjectMap.get(statisticalField));
            if (StringUtils.isNotBlank(fieldValue)) {
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        stringObjectMap.put("percentageTotal", fieldList.size() + "/" + list.size());
    }

}
